#ifndef SELECTION_HEADER_VIZMF_PROJECT
#define SELECTION_HEADER_VIZMF_PROJECT

#include <string>
#include <vector>
//#include <deque>
#include <iterator>
#include <set>
#include <algorithm>
#include <functional>

#include <src/core/workspace/molecule/molecule.h>   // includes also atom.h and bond.h as well as proper bond specification given in Molecule
#include <src/core/workspace/lattice.h>
#include <src/core/workspace/workspace.h>


/*
  Wrapper iterator that remembers a container whose object it points
*/
template <typename ObjectIterator, typename ContainerType>
class container_iterator : public ObjectIterator {
public:
  typedef ContainerType cont_type;
  typedef ObjectIterator iterator;
  typedef typename ObjectIterator::pointer pointer;

public:
  container_iterator() : ObjectIterator (iterator()) {}
  container_iterator(iterator iter, const cont_type& cont) : ObjectIterator(iter), Cont_(cont) {}

  const cont_type& container() const {return Cont_;}  // the main purpose of its existence :)

  const pointer& base() const { return ObjectIterator::base();}

protected:
  const cont_type& Cont_;
};

/*
  Wrapper iterator that remembers a container whose object it points: make use of member iterator (tested)
*/
/*
template <typename ObjectIterator, typename ContainerType>
class container_iterator {
public:
  typedef ContainerType cont_type;
  typedef ObjectIterator iterator;

  typedef typename std::iterator_traits<ObjectIterator>::iterator_category iterator_category;
  typedef typename std::iterator_traits<ObjectIterator>::value_type value_type;
  typedef typename std::iterator_traits<ObjectIterator>::difference_type difference_type;
  typedef typename std::iterator_traits<ObjectIterator>::reference reference;
  typedef typename std::iterator_traits<ObjectIterator>::pointer pointer;

public:
  container_iterator() : ObjectIterator (iterator()) {}
  container_iterator(iterator iter, const cont_type& cont) : iter_(iter), Cont_(cont) {}

  container_iterator(const container_iterator& other) : iter_(other.iter_), Cont_(other.Cont_) {}
  container_iterator& operator= (const container_iterator& other) { iter_=other.iter_; Cont_=other.Cont_; return *this;}

  const cont_type& container() const {return Cont_;}  // the main purpose of its existence :)

  /*
    In a non inherited version we have to duplicate all the calls to original iterator
  * /
  reference operator* () const { return *iter_;}
  pointer operator->() const { return iter_.operator->();}
  container_iterator& operator++() {++iter_; return *this;}
  container_iterator operator++(int) { return container_iterator(iter_++);}
//  container_iterator& operator--() {--iter_; return *this;}
//  container_iterator operator--(int) { return container_iterator(iter_--);}

  const pointer& base() const { return iter_.base();}

protected:
  const cont_type& Cont_;
  iterator iter_;
};
*/

template< typename ObjectIterator, typename ContainerType>
inline bool
operator< (const container_iterator<ObjectIterator, ContainerType>& lhs, const container_iterator<ObjectIterator, ContainerType>& rhs)
{
  if(lhs.container().number() < rhs.container().number())
    return true;
  else if(rhs.container().number() < lhs.container().number())
    return false;
  else return lhs.base() < rhs.base();  // the same container
}

template<>
inline bool
operator< (const container_iterator<Molecule::atom_iterator, Molecule>& lhs, const container_iterator<Molecule::atom_iterator, Molecule>& rhs)
{
  if(lhs.container().number() < rhs.container().number())
    return true;
  else if(rhs.container().number() < lhs.container().number())
    return false;
  else return lhs->number() < rhs->number();  // the same container
}

template< typename ObjectIterator, typename ContainerType>
inline bool
operator== (const container_iterator<ObjectIterator, ContainerType>& lhs, const container_iterator<ObjectIterator, ContainerType>& rhs)
{
  return (lhs.container().number() == rhs.container().number() ) && lhs.base() == rhs.bases();
}

template< typename ObjectIterator, typename ContainerType>
inline bool
operator!= (const container_iterator<ObjectIterator, ContainerType>& lhs, const container_iterator<ObjectIterator, ContainerType>& rhs)
{
  return (lhs.container().number() != rhs.container().number() ) || lhs->number() != rhs->number();
}


/*
  Object iterator to iterate a container of iterators to Object and access Object directly
* /
template < typename ObjectInnerIterator >
class object_iterator : public ObjectInnerIterator {
public:
  typedef ObjectInnerIterator iterator;
  typedef typename iterator::value_type inner_iterator;  // assuming value_type is iterator. Concept check is needed in future...
  typedef typename inner_iterator::pointer pointer;
  typedef typename inner_iterator::reference reference;
//  typedef typename iterator::pointer pointer;
//  typedef typename iterator::reference reference;

public:
  object_iterator(iterator iter = iterator()) : ObjectInnerIterator(iter) {}

  reference operator*() const { return *(ObjectInnerIterator::operator*()); }  // double dereference
  pointer operator->() const { return (ObjectInnerIterator::operator*()).operator->();}
//  pointer operator->() const { return (ObjectInnerIterator::operator->()).operator->();}
//  pointer operator->() const { return ObjectInnerIterator::operator->();}

  object_iterator& operator++(){
    ObjectInnerIterator::operator++();
    return *this;
  }

  object_iterator& operator++(int){
    return object_iterator(const_cast<ObjectInnerIterator &> (ObjectInnerIterator::operator++()));
  }


protected:
};
*/

/*
  Object iterator to iterate a container of iterators to Object and access Object directly
*/
template < typename ObjectInnerIterator >
class object_iterator {
public:
  typedef ObjectInnerIterator iterator;
  typedef typename iterator::value_type inner_iterator;  // assuming value_type is iterator. Concept check is needed in future...
  typedef typename inner_iterator::pointer pointer;
  typedef typename inner_iterator::reference reference;
//  typedef typename iterator::pointer pointer;
//  typedef typename iterator::reference reference;

public:
  object_iterator(iterator iter = iterator()) : iter_(iter) {}

  reference operator*() const { return *(*iter_); }  // double dereference
  pointer operator->() const { return (*iter_).operator->();}
//  pointer operator->() const { return (ObjectInnerIterator::operator->()).operator->();}
//  pointer operator->() const { return ObjectInnerIterator::operator->();}

  object_iterator& operator++(){
    ++iter_;
    return *this;
  }

  object_iterator operator++(int){
    return object_iterator(iter_++);
  }

  const pointer& base() const {return (*iter_).base();}

protected:
  iterator iter_;
};


template< typename ObjectInnerIterator>
inline bool
operator==(const object_iterator<ObjectInnerIterator>& lhs, const object_iterator<ObjectInnerIterator>& rhs)
//{ return (*lhs.base()).base() == (*rhs.base()).base();}
{ return lhs.base() == rhs.base();}

template< typename ObjectInnerIterator>
inline bool
operator!=(const object_iterator<ObjectInnerIterator>& lhs, const object_iterator<ObjectInnerIterator>& rhs)
{ return lhs.base() != rhs.base();}

/*
    VizMF UniformSelection class

    A selection composed of uniform iterators to (uniform) objects from one or more (uniform :) containers.
    Ob_iterator returned by obegin()/oend() pair make possible traversal of a selection as a collection
    of objects.
    Internally iterators are stored with reference on containers associated with them (as container_iterator)
*/

template<typename ContainerIterator>
class UniformSelection {
public:
  typedef ContainerIterator iterator;
  typedef typename iterator::iterator inner_iterator;
  typedef typename iterator::cont_type cont_type;
//  typedef const iterator const_iterator;    // not sure it is that easy. now sure - not that easy
  typedef UniformSelection<ContainerIterator> Self_;

protected:
  typedef std::set<iterator> int_container_type;
//  typedef std::vector<iterator> int_container_type;
  typedef typename int_container_type::iterator int_cont_iterator;

  // auxiliary function-object to generate contaiter_iterator's
  // todo: possibly it should be defined outside
  struct conv : public std::unary_function<inner_iterator, iterator> {
    conv(const cont_type& c) : c_(c) {}
    iterator operator() (inner_iterator i) const { return iterator(i, c_); }
  protected:
    const cont_type& c_;
  };

public:
  typedef typename int_container_type::size_type size_type;
  typedef object_iterator<int_cont_iterator> ob_iterator;

public:
  UniformSelection(){}

  // constructing from a range of container_iterator <inner_iterator, Cont>
  UniformSelection(iterator beg, iterator end) : sel_(beg, end) {}

  // take a pair of inner_iterators and wrap all the range into container_iterators
  UniformSelection(inner_iterator beg, inner_iterator end, const cont_type& Cont) {
//    std::transform(beg, end, std::inserter(sel_, sel_.begin()), conv(Cont));
//    typedef std::insert_iterator<int_container_type, typename int_container_type::iterator> ins_type;
    typedef std::insert_iterator<int_container_type> ins_type;
    ins_type i(sel_, sel_.begin());
//    ins_type i(sel_, sel_.end());
    conv c(Cont);
    for(inner_iterator b=beg; b!=end; b++){
      *(i++) = c(b);
    }
  }

  ob_iterator obegin(){ return ob_iterator (sel_.begin()); }
  ob_iterator oend(){ return ob_iterator (sel_.end()); }
//  iterator begin(){ return sel_.begin(); }
//  iterator end(){ return sel_.end(); }
//  const_iterator begin() const { return const_iterator (static_cast<Base_>(this) ); }
//  const_iterator end() const { return const_iterator (); }

public:
    // a couple of convenience functions
    bool empty() const { return sel_.empty(); }
    size_type size() const { return sel_.size(); }

    // set union
    //    UniformSelection& operator+ (const Self_& us) // shulga: be carefull!! don't return reference to local object
    UniformSelection operator+ (const Self_& us){
        Self_ res;
        std::set_union(sel_.begin(), sel_.end(), us.sel_.begin(), us.sel_.end(), std::inserter(res.sel_, res.sel_.begin()) );
        return res;
    }

    // set intersection
    UniformSelection operator* (const UniformSelection& us){
        Self_ res;
        std::set_intersection(sel_.begin(), sel_.end(), us.sel_.begin(), us.sel_.end(), res.sel_.begin());
        return res;
    }

    // set difference
    UniformSelection operator- (const UniformSelection& us){
        Self_ res;
        std::set_difference(sel_.begin(), sel_.end(), us.sel_.begin(), us.sel_.end(), res.sel_.begin());
        return res;
    }

    // check for inclusion
    bool includes(const Self_ & sel){
      return std::includes(sel_.begin(), sel_.end(), sel.sel_.begin(), sel.sel_.end());
    }

    // be carefull!! this function most most probably should be overloaded in concrete classes
    // to substitute base begin()/end() pair to real pair, e.g. atoms_begin()/atoms_end()
    Self_& select_all(const cont_type& Cont){
      return *this = Self_(Cont.begin(), Cont.end(), Cont);
    }

    Self_& select_all(inner_iterator beg, inner_iterator end, const cont_type& Cont) {
      return *this = Self_(beg, end, Cont);
    }

    Self_& insert(inner_iterator beg, inner_iterator end, const cont_type& Cont) {
      return *this = *this + Self_(beg, end, Cont);
    }

protected:
    int_container_type sel_;
};


typedef container_iterator<Molecule::atom_iterator, Molecule> AtomContIterator;
typedef container_iterator<Molecule::bond_iterator, Molecule> BondContIterator;
typedef container_iterator<Lattice::points_const_iterator, Lattice> GridContIterator;

typedef UniformSelection<AtomContIterator> atom_selection;
typedef UniformSelection<BondContIterator> bond_selection;
typedef UniformSelection<GridContIterator> node_selection;


/*
  VizMF Selection class

  As for now (27.10.2011), Selection is a union of a predefined set of selections
  Made template to resolve cyclic dependence of Selection and WorkSpace.
*/

template <typename Workspace>
class Selection {
public:
  typedef ::atom_selection atom_sel_type;
  typedef ::bond_selection bond_sel_type;
  typedef ::node_selection node_sel_type;

  typedef atom_sel_type::ob_iterator atom_leaf_iterator;
  typedef bond_sel_type::ob_iterator bond_leaf_iterator;
  typedef node_sel_type::ob_iterator node_leaf_iterator;

  typedef Workspace workspace;

public:
  // Selection() {} // possibly does not have much sense
  Selection(atom_sel_type atom_sel, bond_sel_type bond_sel, node_sel_type node_sel)
    : atom_sel_(atom_sel),  bond_sel_(bond_sel), node_sel_(node_sel) {}
//  Selection(const Selection& s) {}    // not needed in current implementation

  /*
    Selection should span objects in the workspace
    Select all should select all objects in the workspace
  */
  Selection(workspace* wsp, std::string mask = "*:*:*") : workspace_(wsp) {
    if(workspace_)
      select_all();   // now select everything appropriate from the workspace

    // the mask is skipped for a while :)
  };

  atom_leaf_iterator atoms_begin(){return atom_sel_.obegin();}
  atom_leaf_iterator atoms_end(){return atom_sel_.oend();}

  bond_leaf_iterator bonds_begin(){return bond_sel_.obegin();}
  bond_leaf_iterator bonds_end(){return bond_sel_.oend();}

  node_leaf_iterator nodes_begin(){return node_sel_.obegin();}
  node_leaf_iterator nodes_end(){return node_sel_.oend();}


protected:
  // AtomSelection
  // BondSelection
  // LatticeNodeSelection
  atom_sel_type atom_sel_;
  bond_sel_type bond_sel_;
  node_sel_type node_sel_;
  workspace * workspace_;

public:
  // set union
  Selection operator+ (const Selection& s) const {
      Selection res(*this);
      res.atom_sel_ = res.atom_sel_ + s.atom_sel_;
      res.bond_sel_ = res.bond_sel_ + s.bond_sel_;
      res.node_sel_ = res.node_sel_ + s.node_sel_;
      return res;
  }

  // set intersection
  Selection operator* (const Selection& s) const {
      Selection res(*this);
      res.atom_sel_ = res.atom_sel_ * s.atom_sel_;
      res.bond_sel_ = res.bond_sel_ * s.bond_sel_;
      res.node_sel_ = res.node_sel_ * s.node_sel_;
      return res;
  }

  // set difference
  Selection operator- (const Selection& s){
      Selection res(*this);
      res.atom_sel_ = res.atom_sel_ - s.atom_sel_;
      res.bond_sel_ = res.bond_sel_ - s.bond_sel_;
      res.node_sel_ = res.node_sel_ - s.node_sel_;
      return res;
  }

  // check for inclusion
  bool includes (const Selection& s) const {
      return atom_sel_.includes(s.atom_sel_) &&
              bond_sel_.includes(s.bond_sel_) &&
              node_sel_.includes(s.node_sel_);
  }

  // select all
  Selection& select_all(){
      // first, procede with molecules' atoms and bonds
      typedef typename workspace::molecule_iterator_type MI;
      for(MI i = workspace_ -> moleculesBegin(), e = workspace_ -> moleculesEnd(); i!=e; i++){
          atom_sel_ = atom_sel_ + atom_sel_type().select_all(i->atomsBegin(), i->atomsEnd(), *i);
          bond_sel_ = bond_sel_ + bond_sel_type().select_all(i->bondsBegin(), i->bondsEnd(), *i);
/*          atom_sel_.select_all(i->atomsBegin(), i->atomsEnd(), *i);
            atom_sel_ = atom_sel_type().select_all(i->atomsBegin(), i->atomsEnd(), *i) +
                atom_sel_type().select_all(i->atomsBegin(), i->atomsEnd(), *i);
          break;
*/
      }

      // then. procede with lattices' modes
      typedef typename workspace::lattice_iterator_type LI;
      for(LI i = workspace_ -> latticesBegin(), e = workspace_ -> latticesEnd(); i!=e; i++){
          node_sel_ = node_sel_ + node_sel_type().select_all(i->pointsBegin(), i->pointsEnd(), *i);
      }

      return *this;
  }

  // invert selection
  Selection invert(){
      Selection res(workspace_);
      res.select_all();
      res = res - *this;
      return res;
  }

  // convenience functions
  // conversion constuctors
  Selection(const atom_sel_type& as){
    atom_sel_ = as;
  }
  Selection(const bond_sel_type& bs){
    bond_sel_ = bs;
  }
  Selection(const node_sel_type& ns){
    node_sel_ = ns;
  }

  const atom_sel_type atomSelection() const { return atom_sel_;}
  const bond_sel_type bondSelection() const { return bond_sel_;}
  const node_sel_type nodeSelection() const { return node_sel_;}

};


/*
todo:
======
1) "select something" functionality, e.g. by type, by SMARTS, etc.
*/

#endif // SELECTION_HEADER_VIZMF_PROJECT


